﻿#region Copyright & License
/*
   Copyright 2009-2010 Stepan Adamec (adamec@yasas.org)

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License. 
 */
#endregion

namespace See.Sharper {
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Text;
  using System.Threading;

  /// <summary>
  /// Extensions of frequently used classes in 'System.Collections.*' namespace.
  /// </summary>
  public static class Sharper_Collections {
    #region Transform [non-generic]

    /// <summary>
    /// Iterates through the enumeration transforming each entry into a new value using the <paramref name="transformer"/>, returning a list of transformed values.
    /// <remarks>This is a non-generic synchronous implementation.</remarks>
    /// </summary>
    /// <param name="self">The source enumeration.</param>
    /// <param name="transformer">The transformer returning the output value.</param>
    /// <returns>A list of transformed values.</returns>
    public static IList Collect(this IEnumerable self, Func<object, object> transformer) {
      List<object> list = new List<object>();

      self.Each(
        it => list.Add(transformer(it))
      );

      return list;
    }

    /// <summary>
    /// Iterates through the enumeration transforming each entry into a new value using the <paramref name="transformer"/>, returning a list of transformed values.
    /// <remarks>This is a non-generic synchronous/parallel implementation (depending on <paramref name="parallel"/> parameter).</remarks>
    /// </summary>
    /// <param name="self">The source enumeration.</param>
    /// <param name="parallel">If true, method will run in parallel.</param>
    /// <param name="transformer">The transformer returning the output value.</param>
    /// <returns>A list of transformed values.</returns>
    public static IList Collect(this IEnumerable self, bool parallel, Func<object, object> transformer) {
      if (parallel) {
        return self.CollectParallel(transformer);
      }

      return self.Collect(transformer);
    }

    /// <summary>
    /// Iterates through the enumeration transforming each entry into a new value using the <paramref name="transformer"/>, returning a list of transformed values.
    /// <remarks>This is a non-generic parallel implementation.</remarks>
    /// </summary>
    /// <param name="self">The source enumeration.</param>
    /// <param name="transformer">The transformer returning the output value.</param>
    /// <returns>A list of transformed values.</returns>
    public static IList CollectParallel(this IEnumerable self, Func<object, object> transformer) {
      List<object> list = new List<object>();

      FuncCallback<object, object> callback = new FuncCallback<object, object>(
        delegate(object target, object stepResult) {
          list.Add(stepResult);
        }
      );

      new FuncTTResultParallelizer<object, object, List<object>>(transformer, callback).With(parallelizer => {
        self.Each(
          it => parallelizer.Queue(it)
        );
        parallelizer.WaitForAll();
      });

      return list;
    }

    #endregion

    #region Transform

    /// <summary>
    /// Iterates through the enumeration transforming each entry into a new value using the <paramref name="transformer"/>, returning a list of transformed values.
    /// <remarks>This is a generic synchronous implementation.</remarks>
    /// <example><code>
    /// System.Console.WriteLine(
    ///   new int[] { 1, 2, 3, 4 }.Collect<int, int>(i => i * 2).ToListString()
    /// );
    /// Output: 2,4,6,8
    /// </code></example>
    /// </summary>
    /// <typeparam name="I">Type of the input items.</typeparam>
    /// <typeparam name="O">Type of the output (transformed) items.</typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="transformer">The transformer returning the output value.</param>
    /// <returns>A list of transformed values.</returns>
    public static IList<O> Collect<I, O>(this IEnumerable<I> self, Func<I, O> transformer) {
      List<O> list = new List<O>();

      self.Each<I>(
        it => list.Add(transformer(it))
      );

      return list;
    }

    /// <summary>
    /// Iterates through the enumeration transforming each entry into a new value using the <paramref name="transformer"/>, returning a list of transformed values.
    /// <remarks>This is a generic synchronous/parallel implementation (depending on <paramref name="parallel"/> parameter).</remarks>
    /// </summary>
    /// <typeparam name="I">Type of the input items.</typeparam>
    /// <typeparam name="O">Type of the output (transformed) items.</typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="parallel">If true, method will run in parallel.</param>
    /// <param name="transformer">The transformer returning the output value.</param>
    /// <returns>A list of transformed values.</returns>
    public static IList<O> Collect<I, O>(this IEnumerable<I> self, bool parallel, Func<I, O> transformer) {
      if (parallel) {
        return self.CollectParallel<I, O>(transformer);
      }

      return self.Collect<I, O>(transformer);
    }

    /// <summary>
    /// Iterates through the enumeration transforming each entry into a new value using the <paramref name="transformer"/>, returning a list of transformed values.
    /// <remarks>This is a generic parallel implementation.</remarks>
    /// </summary>
    /// <typeparam name="I">Type of the input items.</typeparam>
    /// <typeparam name="O">Type of the output (transformed) items.</typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="transformer">The transformer returning the output value.</param>
    /// <returns>A list of transformed values.</returns>
    public static IList<O> CollectParallel<I, O>(this IEnumerable<I> self, Func<I, O> transformer) {
      List<O> list = new List<O>();

      FuncCallback<I, O> callback = new FuncCallback<I, O>(
        delegate(I target, O stepResult) {
          list.Add(stepResult);
        }
      );

      new FuncTTResultParallelizer<I, O, List<O>>(transformer, callback).With(parallelizer => {
        self.Each(
          it => parallelizer.Queue(it)
        );
        parallelizer.WaitForAll();
      });

      return list;
    }
    
    /// <summary>
    /// Sorts all enumeration members into groups determined by the supplied key <paramref name="mapper"/>.
    /// The mapper should return the key that this item should be grouped by. 
    /// <example><code>
    /// new int[] { 1, 2, 1, 3, 4, 5, 3 }.Unique(i => i).GroupBy(i => i.IsEven()).With(groups => {
    ///   groups.Keys.Each<bool>(key => {
    ///     System.Console.WriteLine("{0}: [{1}]", new object[] { (key == true) ? "even" : "odd ", groups[key].ToListString() });
    ///   });
    /// });
    /// Output: even: [1,4,5]; odd : [2,3]
    /// </code></example>
    /// </summary>
    /// <typeparam name="I">Type of the enumerated items.</typeparam>
    /// <typeparam name="K">Type of the mapping key.</typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="mapper">The key mapper.</param>
    /// <returns>A new dictionary grouped by keys.</returns>
    public static IDictionary<K, List<I>> GroupBy<I, K>(this IEnumerable<I> self, Func<I, K> mapper) {
      Dictionary<K, List<I>> map = new Dictionary<K, List<I>>();
      
      self.Each<I>(it => {
        mapper(it).With(key => {
          if (!map.ContainsKey(key)) {
            map.Add(
              key, new List<I>()
            );
          }
          map[key].Add(it);
        });
      });
      
      return map;
    }

    /// <summary>
    /// Method for making a collection unique using the key <paramref name="mapper"/>.
    /// <example><code>
    /// System.Console.WriteLine(
    ///   new int[] { 1, 2, 1, 3, 4, 5, 3 }.Unique(i => i).ToListString()
    /// );
    /// Output: 1,2,3,4,5
    /// </code></example>
    /// </summary>
    /// <typeparam name="K">Type of the unique key.</typeparam>
    /// <typeparam name="I">Type of the enumerated items.</typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="mapper">Delegate used to determine the unique key.</param>
    /// <returns>Collection without any duplicates.</returns>
    public static IList<I> Unique<K, I>(this IEnumerable<I> self, Func<I, K> mapper) {
      Dictionary<K, I> map = new Dictionary<K, I>();

      self.Each<I>(it => {
        mapper(it).With<K>(key => {
          if (!map.ContainsKey(key)) {
            map.Add(key, it);
          }
        });
      });

      return new List<I>(map.Values);
    }

    #endregion

    #region Iterate [non-generic]

    /// <summary>
    /// Iterates through the enumeration, executing an <paramref name="action"/> on each item.
    /// <remarks>This is a non-generic synchronous implementation.</remarks>
    /// </summary>
    /// <param name="self">The source enumeration.</param>
    /// <param name="action">Action to be executed.</param>
    public static void Each(this IEnumerable self, Action<object> action) {
      foreach (object each in self) {
        action(each);
      }
    }

    /// <summary>
    /// Iterates through the enumeration, executing an <paramref name="action"/> on each item.
    /// <remarks>This is a non-generic synchronous/parallel implementation (depending on <paramref name="parallel"/> parameter).</remarks>
    /// </summary>
    /// <param name="self">The source enumeration.</param>
    /// <param name="parallel">If true, method will run in parallel.</param>
    /// <param name="action">Action to be executed.</param>
    public static void Each(this IEnumerable self, bool parallel, Action<object> action) {
      if (parallel) {
        self.EachParallel(action);
      } else {
        self.Each(action);
      }
    }

    /// <summary>
    /// Iterates through the enumeration, executing an <paramref name="action"/> on each item.
    /// <remarks>This is a non-generic parallel implementation.</remarks>
    /// </summary>
    /// <param name="self">The source enumeration.</param>
    /// <param name="action">Action to be executed.</param>
    public static void EachParallel(this IEnumerable self, Action<object> action) {
      new ActionTParallelizer<object>(action).With(parallelizer => {
        self.Each(
          it => parallelizer.Queue(it)
        );
        parallelizer.WaitForAll();
      });
    }

    /// <summary>
    /// Iterates through the enumeration, executing an <paramref name="action"/> on each item, passing the item's index (a counter starting at zero) to the action.
    /// <remarks>This is a non-generic synchronous implementation.</remarks>
    /// </summary>
    /// <param name="self">The source enumeration.</param>
    /// <param name="action">Action to be executed.</param>
    public static void EachWithIndex(this IEnumerable self, Action<object, int> action) {
      lock (self) {
        int i = 0;

        foreach (object each in self) {
          action(each, i++);
        }
      }
    }

    /// <summary>
    /// Iterates through the enumeration, executing an <paramref name="action"/> on each item, passing the item's index (a counter starting at zero) to the action.
    /// <remarks>This is a non-generic synchronous/parallel implementation (depending on <paramref name="parallel"/> parameter).</remarks>
    /// </summary>
    /// <param name="self">The source enumeration.</param>
    /// <param name="parallel">If true, method will run in parallel.</param>
    /// <param name="action">Action to be executed.</param>
    public static void EachWithIndex(this IEnumerable self, bool parallel, Action<object, int> action) {
      if (parallel) {
        self.EachWithIndexParallel(action);
      } else {
        self.EachWithIndex(action);
      }
    }

    /// <summary>
    /// Iterates through the enumeration, executing an <paramref name="action"/> on each item, passing the item's index (a counter starting at zero) to the action.
    /// <remarks>This is a non-generic parallel implementation.</remarks>
    /// </summary>
    /// <param name="self">The source enumeration.</param>
    /// <param name="action">Action to be executed.</param>
    public static void EachWithIndexParallel(this IEnumerable self, Action<object, int> action) {
      new ActionT1T2Parallelizer<object, int>(action).With(parallelizer => {
        int index = 0;

        self.Each(
          it => parallelizer.Queue(it, index++)
        );
        parallelizer.WaitForAll();
      });
    }

    #endregion

    #region Iterate

    /// <summary>
    /// Iterates through the enumeration, executing an <paramref name="action"/> on each item.
    /// <remarks>This is a generic synchronous implementation.</remarks>
    /// </summary>
    /// <typeparam name="I">Type of the enumerated items.</typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="action">Action to be executed.</param>
    public static void Each<I>(this IEnumerable<I> self, Action<I> action) {
      lock (self) {
        foreach (I each in self) {
          action(each);
        }
      }
    }

    /// <summary>
    /// Iterates through the enumeration, executing an <paramref name="action"/> on each item, passing the item's index (a counter starting at zero) to the action.
    /// <remarks>This is a generic synchronous/parallel implementation (depending on <paramref name="parallel"/> parameter).</remarks>
    /// </summary>
    /// <typeparam name="I">Type of the enumerated items.</typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="parallel">If true, method will run in parallel.</param>
    /// <param name="action">Action to be executed.</param>
    public static void Each<I>(this IEnumerable<I> self, bool parallel, Action<I> action) {
      if (parallel) {
        self.EachParallel<I>(action);
      } else {
        self.Each<I>(action);
      }
    }

    /// <summary>
    /// Iterates through the enumeration, executing an <paramref name="action"/> on each item.
    /// <remarks>This is a generic parallel implementation.</remarks>
    /// </summary>
    /// <typeparam name="I">Type of the enumerated items.</typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="action">Action to be executed.</param>
    public static void EachParallel<I>(this IEnumerable<I> self, Action<I> action) {
      new ActionTParallelizer<I>(action).With(parallelizer => {
        self.Each<I>(
          it => parallelizer.Queue(it)
        );
        parallelizer.WaitForAll();
      });
    }

    #endregion

    /// <summary>
    /// Iterates through the enumeration, executing an <paramref name="action"/> on each item, passing the item's index (a counter starting at zero) to the action.
    /// <remarks>This is a generic synchronous implementation.</remarks>
    /// </summary>
    /// <typeparam name="I">Type of the enumerated items.</typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="action">Action to be executed.</param>
    public static void EachWithIndex<I>(this IEnumerable<I> self, Action<I, int> action) {
      lock (self) {
        int i = 0;

        foreach (I each in self) {
          action(each, i++);
        }
      }
    }

    /// <summary>
    /// Iterates through the enumeration, executing an <paramref name="action"/> on each item, passing the item's index (a counter starting at zero) to the action.
    /// <remarks>This is a generic synchronous/parallel implementation (depending on <paramref name="parallel"/> parameter).</remarks>
    /// </summary>
    /// <typeparam name="I">Type of the enumerated items.</typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="parallel">If true, method will run in parallel.</param>
    /// <param name="action">Action to be executed.</param>
    public static void EachWithIndex<I>(this IEnumerable<I> self, bool parallel, Action<I, int> action) {
      if (parallel) {
        self.EachWithIndexParallel<I>(action);
      } else {
        self.EachWithIndex<I>(action);
      }
    }

    /// <summary>
    /// Iterates through the enumeration, executing an <paramref name="action"/> on each item, passing the item's index (a counter starting at zero) to the action.
    /// <remarks>This is a generic parallel implementation.</remarks>
    /// </summary>
    /// <typeparam name="I">Type of the enumerated items.</typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="action">Action to be executed.</param>
    public static void EachWithIndexParallel<I>(this IEnumerable<I> self, Action<I, int> action) {
      new ActionT1T2Parallelizer<I, int>(action).With(parallelizer => {
        int index = 0;

        self.Each<I>(
          it => parallelizer.Queue(it, index++)
        );
        parallelizer.WaitForAll();
      });
    }
    
    #region Search [non-generic]

    /// <summary>
    /// Counts the number of occurrences of the given value inside the enumeration.
    /// </summary>
    /// <param name="self">The source enumeration.</param>
    /// <param name="filter"></param>
    /// <returns></returns>
    public static int Count(this IEnumerable self, Func<object, bool> filter) {
      int count = 0;

      foreach (object each in self) {
        if (filter(each)) count++;
      }

      return count;
    }

    /// <summary>
    /// Counts the number of occurrences of the given value inside the enumeration.
    /// </summary>
    /// <param name="self">The source enumeration.</param>
    /// <param name="parallel"></param>
    /// <param name="filter"></param>
    /// <returns></returns>
    public static int Count(this IEnumerable self, bool parallel, Func<object, bool> filter) {
      if (parallel) {
        return self.CountParallel(filter);
      }

      return self.Count(filter);
    }

    /// <summary>
    /// Counts the number of occurrences of the given value inside the enumeration.
    /// </summary>
    /// <param name="self">The source enumeration.</param>
    /// <param name="filter"></param>
    /// <returns></returns>
    public static int CountParallel(this IEnumerable self, Func<object, bool> filter) {
      int count = 0;

      FuncCallback<object, bool> callback = new FuncCallback<object, bool>(
        delegate(object target, bool stepResult) {
          if (stepResult) {
            Interlocked.Increment(ref count);
          }
        }
      );

      new FuncTTResultParallelizer<object, bool, int>(filter, callback).With(parallelizer => {
        self.Each(
          it => parallelizer.Queue(it)
        );
        parallelizer.WaitForAll();
      });

      return count;
    }

    #endregion

    #region Search

    /// <summary>
    /// Counts the number of occurrences of the given value inside the enumeration.
    /// </summary>
    /// <typeparam name="I"></typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="filter"></param>
    /// <returns></returns>
    public static int Count<I>(this IEnumerable<I> self, Func<I, bool> filter) {
      int count = 0;

      foreach (I each in self) {
        if (filter(each)) count++;
      }
      
      return count;
    }

    /// <summary>
    /// Counts the number of occurrences of the given value inside the enumeration.
    /// </summary>
    /// <typeparam name="I"></typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="parallel"></param>
    /// <param name="filter"></param>
    /// <returns></returns>
    public static int Count<I>(this IEnumerable<I> self, bool parallel, Func<I, bool> filter) {
      if (parallel) {
        return self.CountParallel<I>(filter);
      }

      return self.Count<I>(filter);
    }

    /// <summary>
    /// Counts the number of occurrences of the given value inside the enumeration.
    /// </summary>
    /// <typeparam name="I"></typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="filter"></param>
    /// <returns></returns>
    public static int CountParallel<I>(this IEnumerable<I> self, Func<I, bool> filter) {
      int count = 0;

      FuncCallback<I, bool> callback = new FuncCallback<I, bool>(
        delegate(I target, bool stepResult) {
          if (stepResult) {
            Interlocked.Increment(ref count);
          }
        }
      );

      new FuncTTResultParallelizer<I, bool, int>(filter, callback).With(parallelizer => {
        self.Each<I>(
          it => parallelizer.Queue(it)
        );
        parallelizer.WaitForAll();
      });

      return count;
    }

    /// <summary>
    /// Finds the first value matching the <paramref name="filter"/> condition.
    /// <remarks>This is a generic synchronous implementation.</remarks>
    /// </summary>
    /// <typeparam name="I">Type of the enumerated items.</typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="filter">Delegate that should return true for the item matching the filtering criteria.</param>
    /// <returns>First value matching the <paramref name="filter"/> condition.</returns>
    public static I Find<I>(this IEnumerable<I> self, Func<I, bool> filter) {
      foreach (I each in self) {
        if (filter(each)) return each;
      }

      return default(I);
    }

    /// <summary>
    /// Finds all values matching the <paramref name="filter"/> condition.
    /// <remarks>This is a generic synchronous implementation.</remarks>
    /// </summary>
    /// <typeparam name="I">Type of the enumerated items.</typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="filter">Delegate that should return true all items matching the filtering criteria.</param>
    /// <returns>All values matching the <paramref name="filter"/> condition.</returns>
    public static IList<I> FindAll<I>(this IEnumerable<I> self, Func<I, bool> filter) {
      List<I> found = new List<I>();

      foreach (I each in self) {
        if (filter(each)) found.Add(each);
      }

      return found;
    }

    /// <summary>
    /// Finds all values matching the <paramref name="filter"/> condition.
    /// <remarks>This is a generic synchronous/parallel implementation (depending on <paramref name="parallel"/> parameter).</remarks>
    /// </summary>
    /// <typeparam name="I">Type of the enumerated items.</typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="parallel">If true, method will run in parallel.</param>
    /// <param name="filter">Delegate that should return true all items matching the filtering criteria.</param>
    /// <returns>All values matching the <paramref name="filter"/> condition.</returns>
    public static IList<I> FindAll<I>(this IEnumerable<I> self, bool parallel, Func<I, bool> filter) {
      if (parallel) {
        return self.FindAllParallel<I>(filter);
      }

      return self.FindAll<I>(filter);
    }

    /// <summary>
    /// Finds all values matching the <paramref name="filter"/> condition.
    /// <remarks>This is a generic parallel implementation.</remarks>
    /// </summary>
    /// <typeparam name="I">Type of the enumerated items.</typeparam>
    /// <param name="self">The source enumeration.</param>
    /// <param name="filter">Delegate that should return true all items matching the filtering criteria.</param>
    /// <returns>All values matching the <paramref name="filter"/> condition.</returns>
    public static IList<I> FindAllParallel<I>(this IEnumerable<I> self, Func<I, bool> filter) {
      List<I> list = new List<I>();

      FuncCallback<I, bool> callback = new FuncCallback<I, bool>(
        delegate(I target, bool stepResult) {
          if (stepResult) {
            list.Add(target);
          }
        }
      );

      new FuncTTResultParallelizer<I, bool, int>(filter, callback).With(parallelizer => {
        self.Each<I>(
          it => parallelizer.Queue(it)
        );
        parallelizer.WaitForAll();
      });

      return list;
    }

    #endregion

    #region List

    /// <summary>
    /// Returns the string representation of the given enumeration using default separator.
    /// </summary>
    /// <typeparam name="I">Type of the enumerated items.</typeparam>
    /// <param name="self">The enumeration.</param>
    /// <returns>The string representation</returns>
    public static string ToListString<I>(this IEnumerable<I> self) {
      return ToListString<I>(self, ",", false);
    }

    /// <summary>
    /// Returns the string representation of the given enumeration using the given separator.
    /// </summary>
    /// <typeparam name="I">Type of the enumerated items.</typeparam>
    /// <param name="self">The enumeration.</param>
    /// <param name="separator">String used to separate the items of the given enumeration.</param>
    /// <returns>The string representation</returns>
    public static string ToListString<I>(this IEnumerable<I> self, string separator) {
      return ToListString<I>(self, separator, false);
    }
    
    /// <summary>
    /// Returns the string representation of the given enumeration using the given separator.
    /// </summary>
    /// <typeparam name="I">Type of the enumerated items.</typeparam>
    /// <param name="self">The enumeration.</param>
    /// <param name="separator">String used to separate the items of the given enumeration.</param>
    /// <param name="keepLastSeparator">If true, the last separator will not be trimmed from the result.</param>
    /// <returns>The string representation</returns>
    public static string ToListString<I>(this IEnumerable<I> self, string separator, bool keepLastSeparator) {
      if (String.IsNullOrEmpty(separator)) {
        separator = ",";
      }
      StringBuilder builder = new StringBuilder();

      self.Each<I>(
        it => builder.Append(Convert.ToString(it)).Append(separator)
      );
      if (!keepLastSeparator && (builder.Length > 0)) {
        builder.Length = (builder.Length - separator.Length);
      }

      return builder.ToString();
    }

    #endregion
  }
}
